package com.shinbada.modules.competitor.utils;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 树通用方法类
 *
 * @author
 * @since
 */
public class TreeUtilCommon {

    /**
     * 组装实体树
     *
     * @param nodes            要组装成树的实体List
     * @param className        实体全类名
     * @param idName           id字段名 类型为基本类型，自动封装类型（如Long、Integer），String
     * @param pidName          父id字段名 类型为基本类型，自动封装类型（如Long、Integer），String
     * @param childrenListName 子节点存放List的字段名
     * @return
     * @throws Exception
     * @author
     * @since 2016年11月9日 下午3:42:57
     */
    public static <T> List<T> buildTree(List<T> nodes, String className,
                                        String idName, String pidName, String childrenListName)
            throws Exception {
        // 树
        List<T> tree = new ArrayList<T>();
        // 遍历
        for (T node : nodes) {
            tree = findChildren(node, tree, className, idName, pidName,
                    // 从树上找到node的孩子节点并返回一颗新树
                    childrenListName);
            // 从树上找到node的父节点并组装，如果没有父节点，则返回false
            boolean isChild = isChild(node, tree, className, idName, pidName,
                    childrenListName);
            // 树上没有父节点，把node挂到树上
            if (!isChild) {
                tree.add(node);
            }
        }
        return tree;
    }

    /**
     * 根据ID从实体树上获取节点
     *
     * @param tree             实体树
     * @param id               id值
     * @param className        实体全类名
     * @param idName           id字段名 类型为基本类型，自动封装类型（如Long、Integer），String
     * @param pidName          父id字段名 类型为基本类型，自动封装类型（如Long、Integer），String
     * @param childrenListName 子节点存放List的字段名
     * @return 实体节点
     * @throws Exception
     * @author
     * @since
     */
    public static <T> T getNodeById(List<T> tree, Object id, String className,
                                    String idName, String pidName, String childrenListName)
            throws Exception {
        if (tree == null || tree.size() == 0) {
            return null;
        }
        for (T node : tree) {
            Class c = Class.forName(className);
            // 获取id的get方法
            Method getId = c.getMethod("get" + upFirst(idName));
            // 调用id的get方法获取node的id值
            Object node_id = getId.invoke(node);

            // TODO 先不处理类型的问题，直接用简单类型、封装类型、String判断
            if (node_id.toString().equals(id.toString())) {
                return node;
            } else {
                // 获取childrenList的get方法
                Method getChildrenList = c.getMethod("get"
                        + upFirst(childrenListName));
                // 调用childrenList的get方法获取node的childrenList值
                List childrenList = (List) getChildrenList.invoke(node);
                T n = (T) getNodeById(childrenList, id, className, idName,
                        pidName, childrenListName);
                if (n != null) {
                    return n;
                }
            }
        }
        return null;
    }

    private static <T> boolean isChild(T node, List<T> tree, String className,
                                       String idName, String pidName, String childrenListName)
            throws ClassNotFoundException, NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        Class c = Class.forName(className);
        // 获取pid的get方法
        Method getPid = c.getMethod("get" + upFirst(pidName));
        // 调用pid的get方法获取node的pid值
        Object node_pid = getPid.invoke(node);
        if (node_pid == null) {
            return false;
        }
        // 要遍历的list长度为0
        if (tree == null || tree.size() == 0) {
            return false;
        }
        for (T n : tree) {
            // 获取childrenList的get方法
            Method getChildrenList = c.getMethod("get"
                    + upFirst(childrenListName));
            // 调用childrenList的get方法获取node的childrenList值
            List childrenList = (List) getChildrenList.invoke(n);
            // n是否是node的父亲
            if (isParent(node, n, className, idName, pidName, childrenListName)) {
                if (childrenList == null) {
                    // 获取childrenList的set方法
                    Method setChildrenList = c.getMethod("set"
                            + upFirst(childrenListName), List.class);
                    setChildrenList.invoke(n, new ArrayList<T>());
                }
                // 调用childrenList的get方法获取node的childrenList值
                childrenList = (List) getChildrenList.invoke(n);
                childrenList.add(node);
                return true;
            } else {// 不是，则遍历n的子节点
                if (isChild(node, childrenList, className, idName, pidName,
                        childrenListName)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * tree的部分根节点是node的子节点
     *
     * @param node
     * @param tree
     * @param className
     * @param idName
     * @param pidName
     * @param childrenListName
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws ClassNotFoundException
     */
    private static <T> List<T> findChildren(T node, List<T> tree,
                                            String className, String idName, String pidName,
                                            String childrenListName) throws IllegalAccessException,
            IllegalArgumentException, InvocationTargetException,
            NoSuchMethodException, SecurityException, ClassNotFoundException {
        // 新树
        List<T> newTree = new ArrayList<T>();
        for (T n : tree) {
            if (isParent(n, node, className, idName, pidName, childrenListName)) {
                Class c = Class.forName(className);
                // 获取childrenList的get方法
                Method getChildrenList = c.getMethod("get"
                        + upFirst(childrenListName));
                // 调用childrenList的get方法获取node的childrenList值
                List childrenList = (List) getChildrenList.invoke(node);
                if (childrenList == null) {
                    // 获取childrenList的set方法
                    Method setChildrenList = c.getMethod("set"
                            + upFirst(childrenListName), List.class);
                    setChildrenList.invoke(node, new ArrayList<T>());
                }
                childrenList = (List) getChildrenList.invoke(node);
                childrenList.add(n);
                // 不是孩子
            } else {
                newTree.add(n);
            }
        }
        return newTree;
    }

    /**
     * node是n的父节点
     *
     * @param n
     * @param node
     * @param className
     * @param idName
     * @param pidName
     * @param childrenListName
     * @param <T>
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private static <T> boolean isParent(T n, T node, String className,
                                        String idName, String pidName, String childrenListName)
            throws ClassNotFoundException, NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        Class c = Class.forName(className);
        // 获取id的get方法
        Method getId = c.getMethod("get" + upFirst(idName));
        // 获取pid的get方法
        Method getPid = c.getMethod("get" + upFirst(pidName));
        // 调用id的get方法获取node的id值
        Object node_id = getId.invoke(node);
        // 调用pid的get方法获取n的pid值
        Object n_pid = getPid.invoke(n);
        // 根据值的类型比较是否相同
        // TODO 先不处理类型的问题，直接用简单类型、封装类型、String判断
        if (n_pid != null && n_pid.toString().equals(node_id.toString())) {
            return true;
        }
        return false;
    }

    /**
     * 首字母大写
     *
     * @param name
     * @return
     */
    private static String upFirst(String name) {
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        return name;
    }

}
